Skip to content

Better chunking strategies for constlat intersections and zonal routines. - #1624

Open
cmdupuis3 wants to merge 78 commits into
mainfrom
cmd/accusphere3
Open

Better chunking strategies for constlat intersections and zonal routines.#1624
cmdupuis3 wants to merge 78 commits into
mainfrom
cmd/accusphere3

Conversation

@cmdupuis3

@cmdupuis3 cmdupuis3 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

This PR contains two post-accusphere optimizations, eliminating low-level hard materializations by using a vector-based masking strategy rather than individual conditionals, and reduced zonal_mean peakmem by building only the candidate faces instead of the whole grid.

Partly addresses #1587

Closes #1650

Overview

Phase A eliminated the largest peak-memory amplifier in the zonal path: the port materialized the whole-grid (n_face, n_max, 2, 3) face-edge array up front (a 5.8× blow-up over the node coordinates, with a ~23 MB build peak on a 28k-face grid) even though each latitude only touches ~1% of faces. I added an @njit(parallel=True) subset builder, _get_cartesian_face_edge_nodes_array_subset, that constructs edges only for the candidate faces of a given latitude/band — bit-identical to indexing the full array — and rewired _compute_non_conservative_zonal_mean and _compute_face_band_weights to build per-candidate subsets instead of the whole grid. Peak memory for a 180-latitude zonal_mean dropped from 23.66 MB to 0.27 MB (≈88×) and it ran ~10% faster (no full build, no per-latitude fancy-index copies), verified lossless via a git-stash A/B (the only diff was a pre-existing 4.4e-16 parallel-reduction nondeterminism) with the full zonal/cross-section suite passing.

Phase B moved the six edge/face screeners (constant_lat/lon_intersections_no_extreme, constant_lat/lon_intersections_face_bounds, faces_within_lat/lon_bounds) off @njit and onto plain vectorized NumPy, drawing the boundary so the low-level Numba kernels stay for real geometry while these memory-bound elementwise predicates use NumPy — which is ~2.1× faster here and, unlike an njit kernel that forces a full .values materialization, composes with dask (a single _flatnonzero helper leans on NumPy's array_function protocol so a dask mask reduces block-wise, no explicit dask branch). Call sites pass .data instead of .values so a chunked grid stays lazy, and edge coordinates are gathered positionally (node_z.data[conn.ravel()].reshape(...)) to stay chunk-friendly. Along the way it fixed two real latent bugs: get_edges_at_constant_latitude referenced a nonexistent self.edge_node_z (it raised AttributeError on every call), and both edge paths crashed on chunked grids because xarray can't vindex with a dask indexer. Results are bit-identical to the original per-element loops across 300 randomized trials, dask==numpy, and it's committed as bf0abbe "Lazy intersections".

PR Checklist

General

  • An issue is linked created and linked
  • Add appropriate labels
  • Filled out Overview and Expected Usage (if applicable) sections

Testing

  • Adequate tests are created if there is new functionality
  • Tests cover all possible logical paths in your function
  • Tests are not too basic (such as simply calling a function and nothing else)

Documentation

  • Docstrings have been added to all new functions
  • Docstrings have updated with any function changes

rajeeja added 30 commits June 8, 2026 15:47
…rite intersections, add 241 baseline testsgit status! - most came from accusphere
- benchmarks/geometry_kernels.py: ASV micro-benchmarks for all three
  layers of the EFT intersection stack (_accux_gca, _try_gca_gca_intersection,
  gca_gca_intersection, _accux_constlat, _try_gca_const_lat_intersection,
  gca_const_lat_intersection) plus EFT primitives and point-in-polygon;
  all functions warmed before timing so results reflect steady-state cost
- test/test_plot.py: add test_to_raster_auto_extent verifying that the
  axis limits change and the raster contains finite data
…rectness fixes

Review comments addressed:
- Remove "near-double precision" / "sufficient" overclaims; say "roughly twice
  as accurate" and note the robustness tier boundary clearly
- Explain _lon_bounds_from_vertices is required for UXarray antimeridian
  encoding and cannot be removed
- Add block comment before _no_extreme functions clarifying they are
  pre-existing edge screeners unrelated to the EFT stack
- Document SoS as explicit future work in _point_in_polygon_sphere docstring
- L2 pos_fin/neg_fin: replace ternary with int(); exploit neg=-pos symmetry
- Label computation: drop dead local*0 term, use integer mask arithmetic
- Remove vertex-lat snap from bounds: _face_location_info already captures
  interior arc extrema accurately via the compensated kernel
- _ON_MINOR_ARC_TOL: document intentional 1e-10 vs C++ 1e-8 divergence

Bug fixes:
- on_minor_arc: add antipodal-endpoint guard; a x b = 0 for antipodal inputs
  so every point on the great circle passes the collinearity test (false pos)
- bounds.py: replace mask arithmetic use_ext*z_ext + (1-use_ext)*z_edge with
  plain if/else; 0*NaN = NaN propagates when norm=0, if/else does not
- _point_in_polygon_sphere: ray-nudge now restarts the loop from i=0 so all
  edges are counted with the same ray (mid-loop nudge corrupted crossing parity)

Cleanup:
- Remove _flip_sign, _SIGN_NEG, _SIGN_POS, _SIGN_ZERO dead code from
  point_in_face.py; inline literals in _counts_as_crossing
- Remove _SNAP_TOL_DEG constant and snap_tol_deg parameter throughout bounds.py
- Notebook: fix Grid.get_point_on_face -> get_faces_containing_point; remove
  incorrect geometry.py row from Section 4 table; add accucross_pair and
  acc_sqrt_re to Section 2 building-blocks table
…PI name

- ci/environment.yml: pin tornado<6.5.7 to avoid ssl.SSLError in panel 1.9.3
  on Python 3.11 Windows (conda-forge regression, 2026-06-10)
- intersections.py: remove _gca_gca_intersection_cartesian shim (dead code);
  add comment explaining _snap_const_lat_endpoint snap_sq constant
- test_intersections.py: update 4 call sites to use gca_gca_intersection directly
- spherical-geometry-accuracy.ipynb: fix stale Grid.get_point_on_face ->
  Grid.get_faces_containing_point (2 occurrences)
…rite intersections, add 241 baseline testsgit status! - most came from accusphere
Reconcile diverged accusphere branch. Resolutions:
- intersections.py: restore inline=always on L1 kernels (_accux_constlat,
  _accux_gca) for allocation scalar-replacement
- point_in_face.py: keep restart-loop ray casting (consistent parity),
  adopt named sign constants, drop unused _flip_sign
- arcs.py: keep antipodal-endpoint guard in on_minor_arc
- bounds.py: keep vertex-latitude snapping (snap_tol_deg) path
- computing.py: keep detailed docstring with SIAM/EGUsphere references
Add an LLVM fma intrinsic and route two_prod through a single fused
multiply-add for its error term on hardware that supports it, selected at
import time and validated to be bit-exact against the Veltkamp split. Falls
back to the portable Veltkamp form otherwise, so there is no hard FMA
dependency.

The FMA path is ~2x faster in the compensated geometry kernels (each
two_prod drops from ~17 flops to one FMADD) and is numerically identical:
all 241 AccuSphGeom baseline cases pass unchanged.
Add _accux_constlat_scalar, which takes the arc endpoints as six scalars and
returns the candidate coordinates as scalars instead of two np.empty(3)
arrays. _accux_constlat now wraps it so the array API is unchanged.

Returning scalars lets Numba keep the candidates in registers, so a batch
loop over many edges does no per-point heap allocation. On a 16M-point
const-lat sweep this is ~2.7x faster than the array-returning path and drops
the AccuX/FP64 cost ratio from ~19x to ~7x. Bit-identical results; all 241
AccuSphGeom baseline cases pass.
@cmdupuis3 cmdupuis3 self-assigned this Jul 27, 2026
@cmdupuis3 cmdupuis3 added the scalability Related to scalability & performance efforts label Jul 27, 2026
@cmdupuis3

Copy link
Copy Markdown
Collaborator Author

pre-commit.ci autofix

@cmdupuis3 cmdupuis3 added the benchmarking Related to benchmarks, memory usage, and/or time profiling label Jul 28, 2026
@cmdupuis3 cmdupuis3 added the run-benchmark Run ASV benchmark workflow label Jul 29, 2026
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

ASV Benchmarking

Benchmark Comparison Results

Benchmarks that have improved:

Change Before [3228024] After [a6ecb87] Ratio Benchmark (Parameter)
- 517M 336M 0.65 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
- 626M 335M 0.53 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
- 436M 331M 0.76 mpas_ocean.FaceAreas.peakmem_compute_face_areas('480km')
- 428M 338M 0.79 mpas_ocean.Gradient.peakmem_gradient('480km')
- 22.0±0.2ms 7.36±0.07ms 0.33 mpas_ocean.ZonalAverage.time_zonal_average('120km')
- 4.17±0.04ms 3.42±0.04ms 0.82 mpas_ocean.ZonalAverage.time_zonal_average('480km')

Benchmarks that have stayed the same:

Change Before [3228024] After [a6ecb87] Ratio Benchmark (Parameter)
165±0.5ms 165±0.3ms 1.00 bench_connectivity.Connectivity.time_edge_face('120km')
9.71±0.09ms 9.71±0.04ms 1.00 bench_connectivity.Connectivity.time_edge_face('480km')
161±1ms 161±3ms 1.00 bench_connectivity.Connectivity.time_edge_node('120km')
9.08±0.2ms 9.75±0.4ms 1.07 bench_connectivity.Connectivity.time_edge_node('480km')
161±0.3ms 163±0.7ms 1.01 bench_connectivity.Connectivity.time_face_edge('120km')
9.11±0.04ms 9.19±0.02ms 1.01 bench_connectivity.Connectivity.time_face_edge('480km')
666±3ms 684±10ms 1.03 bench_connectivity.Connectivity.time_face_face('120km')
41.9±0.4ms 41.4±0.1ms 0.99 bench_connectivity.Connectivity.time_face_face('480km')
54.5±2μs 55.5±4μs 1.02 bench_connectivity.Connectivity.time_face_node('120km')
53.6±2μs 53.8±2μs 1.00 bench_connectivity.Connectivity.time_face_node('480km')
373±5μs 380±10μs 1.02 bench_connectivity.Connectivity.time_n_nodes_per_face('120km')
269±5μs 274±7μs 1.02 bench_connectivity.Connectivity.time_n_nodes_per_face('480km')
167±5ms 162±0.3ms 0.97 bench_connectivity.Connectivity.time_node_edge('120km')
9.13±0.06ms 9.16±0.03ms 1.00 bench_connectivity.Connectivity.time_node_edge('480km')
53.2±0.2ms 53.5±0.1ms 1.00 bench_connectivity.Connectivity.time_node_face('120km')
3.59±0.02ms 3.52±0.02ms 0.98 bench_connectivity.Connectivity.time_node_face('480km')
334M 334M 1.00 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
363M 365M 1.01 face_bounds.FaceBounds.peakmem_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
7.44±0.05ms 7.46±0.03ms 1.00 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/mpas/QU/oQU480.231010.nc'))
2.18±0.01ms 2.15±0.01ms 0.99 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/scrip/outCSne8/outCSne8.nc'))
9.12±0.03ms 9.14±0.04ms 1.00 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/geoflow-small/grid.nc'))
1.51±0.01ms 1.50±0.01ms 0.99 face_bounds.FaceBounds.time_face_bounds(PosixPath('/home/runner/work/uxarray/uxarray/test/meshfiles/ugrid/quad-hexagon/grid.nc'))
929±40ns 948±60ns 1.02 geometry_kernels.AccucrossKernels.time_accucross
2.15±0.05μs 2.20±0.04μs 1.02 geometry_kernels.AccucrossKernels.time_accucross_pair
349±20ns 350±9ns 1.00 geometry_kernels.EFTPrimitives.time_acc_sqrt_re
336±20ns 391±30ns ~1.17 geometry_kernels.EFTPrimitives.time_diff_of_products
340±10ns 301±10ns ~0.89 geometry_kernels.EFTPrimitives.time_two_prod
348±20ns 333±10ns 0.96 geometry_kernels.EFTPrimitives.time_two_sum
1.23±0.04μs 1.19±0.02μs 0.96 geometry_kernels.GCAConstLatIntersection.time_accux_constlat_kernel
922±800ns 806±30ns ~0.88 geometry_kernels.GCAConstLatIntersection.time_gca_const_lat_intersection
1.51±0.05μs 1.47±0.03μs 0.97 geometry_kernels.GCAConstLatIntersection.time_try_gca_const_lat_intersection
1.29±0.02μs 1.37±0.04μs 1.06 geometry_kernels.GCAGCAIntersection.time_accux_gca_kernel
1.11±0.06μs 1.12±0.02μs 1.01 geometry_kernels.GCAGCAIntersection.time_gca_gca_intersection
1.67±0.05μs 1.72±0.02μs 1.03 geometry_kernels.GCAGCAIntersection.time_try_gca_gca_intersection
44.5±0.5μs 43.6±1μs 0.98 geometry_kernels.OrientPredicates.time_on_minor_arc
1.03±0.05μs 947±30ns 0.92 geometry_kernels.OrientPredicates.time_orient3d_on_sphere
2.26±0.1ms 2.09±0.01ms 0.92 geometry_samebody.SameBodyConstLat.time_accux_dispatch
664±6μs 668±4μs 1.01 geometry_samebody.SameBodyConstLat.time_accux_kernel
1.42±0.01ms 1.42±0ms 1.00 geometry_samebody.SameBodyConstLat.time_fp64_dispatch
117±4μs 123±7μs 1.06 geometry_samebody.SameBodyConstLat.time_fp64_kernel
32.1±0.01ms 32.1±0.02ms 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_dispatch
7.92±0.1ms 7.72±0.04ms 0.98 geometry_samebody_gcagca.SameBodyGcaGca.time_accux_kernel
30.1±0.9ms 28.4±0.06ms 0.94 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_dispatch
4.57±0.02ms 4.55±0ms 1.00 geometry_samebody_gcagca.SameBodyGcaGca.time_fp64_kernel
639±8ms 646±8ms 1.01 import.Imports.timeraw_import_uxarray
2.51±0.01ms 2.51±0.02ms 1.00 mpas_ocean.CheckNorm.time_check_norm('120km')
1.73±0.01ms 1.74±0.01ms 1.01 mpas_ocean.CheckNorm.time_check_norm('480km')
626±4ms 637±7ms 1.02 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('120km')
37.8±0.3ms 38.7±0.7ms 1.02 mpas_ocean.ConnectivityConstruction.time_face_face_connectivity('480km')
580±20μs 565±10μs 0.97 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('120km')
452±8μs 461±9μs 1.02 mpas_ocean.ConnectivityConstruction.time_n_nodes_per_face('480km')
4.00±0.05ms 3.97±0.06ms 0.99 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('120km')
3.05±0.04ms 3.04±0.02ms 1.00 mpas_ocean.ConstructFaceLatLon.time_cartesian_averaging('480km')
2.84±0.04s 2.90±0.06s 1.02 mpas_ocean.ConstructFaceLatLon.time_welzl('120km')
192±9ms 182±1ms 0.95 mpas_ocean.ConstructFaceLatLon.time_welzl('480km')
11.2±0.03ms 11.6±0.4ms 1.04 mpas_ocean.ConstructTreeStructures.time_ball_tree('120km')
773±20μs 777±10μs 1.00 mpas_ocean.ConstructTreeStructures.time_ball_tree('480km')
8.69±0.03ms 9.59±0.9ms ~1.10 mpas_ocean.ConstructTreeStructures.time_kd_tree('120km')
617±10μs 611±10μs 0.99 mpas_ocean.ConstructTreeStructures.time_kd_tree('480km')
572±6ms 556±3ms 0.97 mpas_ocean.CrossSections.time_const_lat('120km', 1)
294±5ms 284±3ms 0.97 mpas_ocean.CrossSections.time_const_lat('120km', 2)
145±0.8ms 147±2ms 1.01 mpas_ocean.CrossSections.time_const_lat('120km', 4)
416±0.7ms 424±0.4ms 1.02 mpas_ocean.CrossSections.time_const_lat('480km', 1)
213±1ms 215±2ms 1.01 mpas_ocean.CrossSections.time_const_lat('480km', 2)
109±0.5ms 109±0.4ms 1.00 mpas_ocean.CrossSections.time_const_lat('480km', 4)
354M 354M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('120km', 1)
354M 354M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('120km', 2)
354M 354M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('120km', 4)
338M 338M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('480km', 1)
337M 337M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('480km', 2)
338M 337M 1.00 mpas_ocean.CrossSectionsPeakMem.peakmem_const_lat('480km', 4)
23.8±0.1ms 23.7±0.1ms 0.99 mpas_ocean.DualMesh.time_dual_mesh_construction('120km')
2.54±0.06ms 2.64±0.02ms 1.04 mpas_ocean.DualMesh.time_dual_mesh_construction('480km')
350M 350M 1.00 mpas_ocean.FaceAreas.peakmem_compute_face_areas('120km')
71.3±0.05ms 72.0±0.3ms 1.01 mpas_ocean.FaceAreas.time_compute_face_areas('120km')
6.78±0.06ms 6.77±0.04ms 1.00 mpas_ocean.FaceAreas.time_compute_face_areas('480km')
696±7ms 693±8ms 0.99 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', False)
42.3±0.4ms 41.7±0.6ms 0.99 mpas_ocean.GeoDataFrame.time_to_geodataframe('120km', True)
58.8±0.5ms 60.5±0.5ms 1.03 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', False)
4.79±0.1ms 4.56±0.1ms 0.95 mpas_ocean.GeoDataFrame.time_to_geodataframe('480km', True)
351M 351M 1.00 mpas_ocean.Gradient.peakmem_gradient('120km')
204±2ms 204±3ms 1.00 mpas_ocean.Gradient.time_gradient('120km')
13.8±0.1ms 13.8±0.1ms 1.00 mpas_ocean.Gradient.time_gradient('480km')
482±50μs 436±7μs ~0.91 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('120km')
177±5μs 178±6μs 1.01 mpas_ocean.HoleEdgeIndices.time_construct_hole_edge_indices('480km')
350M 350M 1.00 mpas_ocean.Integrate.peakmem_integrate('120km')
329M 329M 1.00 mpas_ocean.Integrate.peakmem_integrate('480km')
495±20μs 468±10μs 0.95 mpas_ocean.Integrate.time_integrate('120km')
397±5μs 413±8μs 1.04 mpas_ocean.Integrate.time_integrate('480km')
138±3ms 134±0.9ms 0.97 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'exclude')
133±4ms 133±1ms 1.00 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'include')
136±1ms 136±1ms 1.00 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('120km', 'split')
10.5±0.2ms 10.1±0.1ms 0.96 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'exclude')
10.0±0.2ms 9.95±0.06ms 0.99 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'include')
10.0±0.1ms 10.2±0.1ms 1.01 mpas_ocean.MatplotlibConversion.time_dataarray_to_polycollection('480km', 'split')
301±8μs 301±10μs 1.00 mpas_ocean.PointInPolygon.time_face_search_lonlat('120km')
263±4μs 274±4μs 1.04 mpas_ocean.PointInPolygon.time_face_search_lonlat('480km')
272±5μs 285±4μs 1.05 mpas_ocean.PointInPolygon.time_face_search_xyz('120km')
251±7μs 250±6μs 1.00 mpas_ocean.PointInPolygon.time_face_search_xyz('480km')
169±0.02ms 172±1ms 1.02 mpas_ocean.RemapDownsample.time_bilinear_remapping
165±0.8ms 167±2ms 1.01 mpas_ocean.RemapDownsample.time_inverse_distance_weighted_remapping
12.6±0.08ms 12.7±0.1ms 1.01 mpas_ocean.RemapDownsample.time_nearest_neighbor_remapping
943±5ms 947±4ms 1.00 mpas_ocean.RemapUpsample.time_bilinear_remapping
29.5±0.2ms 30.3±0.2ms 1.03 mpas_ocean.RemapUpsample.time_inverse_distance_weighted_remapping
10.5±0.2ms 10.6±0.3ms 1.01 mpas_ocean.RemapUpsample.time_nearest_neighbor_remapping
378M 356M 0.94 mpas_ocean.ZonalAveragePeakMem.peakmem_zonal_average('120km')
341M 341M 1.00 mpas_ocean.ZonalAveragePeakMem.peakmem_zonal_average('480km')
331M 325M 0.98 quad_hexagon.QuadHexagon.peakmem_open_dataset
324M 324M 1.00 quad_hexagon.QuadHexagon.peakmem_open_grid
5.79±0.1ms 5.75±0.1ms 0.99 quad_hexagon.QuadHexagon.time_open_dataset
4.79±0.1ms 4.77±0.1ms 1.00 quad_hexagon.QuadHexagon.time_open_grid

@cmdupuis3
cmdupuis3 requested a review from rajeeja July 29, 2026 22:38
@cmdupuis3
cmdupuis3 marked this pull request as ready for review July 29, 2026 22:38
@rajeeja

rajeeja commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Thanks for putting this together. I'll take a look at this and post my comments soon

@rajeeja rajeeja left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified independently: subset builder is bit-identical to whole-grid builder (tested HEALPix + mixed-polygon MPAS), vectorized screeners match brute-force reference, both dask/AttributeError bugs reproduce on main and are fixed here. Two small comments below, non-blocking on correctness but worth addressing before merge.

Comment thread uxarray/core/zonal.py

@erogluorhan erogluorhan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good to me. Just a couple questions:

  • Should we worry about the couple benchmarks that got worse? I believe, no, they shouldn't be directly related to the changes here, but am curious about your thoughts.
  • Could you add test cases to cover the two latent bugs that this PR fixes?

@cmdupuis3

Copy link
Copy Markdown
Collaborator Author

@erogluorhan Pretty sure those benchmark regressions were just variability, they're gone in the new batch. Regression tests are now added for both bugs.

@rajeeja
rajeeja self-requested a review August 7, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmarking Related to benchmarks, memory usage, and/or time profiling run-benchmark Run ASV benchmark workflow scalability Related to scalability & performance efforts

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

Post-accusphere optimized routines (constlat intersections and zonal)

4 participants